/**
 * @file heap_test_senya.h
 *
 * @brief test for heap_senya
 *
 */

#include <iostream>
#include <fstream>
#include <windows.h>
#include <set>

using namespace std;

class Exp : public std::exception
{
private:
  std::string msg;
public :
  Exp() {};
  Exp( const Exp &rhs )
  {
    msg = rhs.msg;
  }
  Exp( char * str)
  {
  	cout << str;
    msg = str;
  }
  virtual ~Exp() throw(){};
  virtual const char * what() const throw()
  {
    return msg.c_str();
  }
};

class ExpBA : public std::exception
{
private:
  std::string msg;
public :
  ExpBA() {};
  ExpBA( const ExpBA &rhs )
  {
    msg = rhs.msg;
  }
  ExpBA( char * str)
  {
  	msg = "bad alloc";
  	cout << msg;
  }
  virtual ~ExpBA() throw(){};
  virtual const char * what() const throw()
  {
    return msg.c_str();
  }
};

#define exception Exp
#define bad_alloc ExpBA

//#include "D:/mem-allocator-876/src/senya/heap_senya.h"
//#include "D:/mem-allocator-876/src/senya/heap_senya.cpp"
//#include "/host/mem-allocator-876/src/kulchenkov/heap_Kulchenkov.c"

#include "D:/mem-allocator-876/src/senya/READ_and_include_me.h"

#define tr(cont, it) for(typeof((cont).begin()) it=(cont).begin(); it != (cont).end(); ++it)

int easytest(heap876 *ph) {
	cout << ph->get_name() << ' ' << ph->get_volume() << std::endl;
	int *arr = (int *) ph->alloc(100*sizeof(int), 'wsef');
	for(int i=0; i<100; ++i)
		arr[i] = i;

	//((heap_senya *)ph)->print_blocks();
	return 0;
}

typedef pair<int, int> ii;
typedef pair<int, ii> iii;

int X = 10;

void test_performance_rand(heap876 *ph, int allocs = 10000, int min_alloc = 1, int max_alloc = 1000) {
	void *ptr[allocs];
	bool OK = true;
	set< iii > Q;
	cout << "performance rand (" << allocs << " allocs of [" << min_alloc << " .. " << max_alloc << "] bytes): " << endl;
	cout << "creating test..." << endl;
	for (int i=0; i < allocs; ++i) {
		iii a(rand()%allocs, ii(rand()%(max_alloc+1-min_alloc) + min_alloc, i));
		iii b(a.first + rand()%allocs + 1, ii(0, i));
		Q.insert(a);
		Q.insert(b);
	}

	double deltaTime;
    static __int64 gTime,gLastTime;
	__int64 freq;

	cout << "testing...  ";
	QueryPerformanceCounter((LARGE_INTEGER *)&gLastTime);
	for(int x=0; x<X; ++x) {
		tr(Q, it) {
			if(it->second.first) {
				ptr[it->second.second] = ph->alloc(it->second.first);

				if(!ptr[it->second.second]) {
					OK = false;
					goto out1;
				}
			} else
				ph->free(ptr[it->second.second]);
		}
	}
out1:
	QueryPerformanceCounter((LARGE_INTEGER *)&gTime);
	QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
    deltaTime = (double)((double)(gTime - gLastTime)/(double)freq);
	if(OK) {
		cout << "[OK]" << endl;
		cout << "TIME = " << deltaTime << endl;
    } else {
		cout << "[fail]" << endl;
    	cout << "alloc returned NULL" << endl;
    }
    cout << endl;
}

void test_valid_rand(heap876 *ph, int allocs = 10000, int min_alloc = 1, int max_alloc = 1000) {
	void *ptr[allocs];
	bool OK = true;
	set< iii > Q;
	cout << "performance rand (" << allocs << " allocs of [" << min_alloc << " .. " << max_alloc << "] bytes): " << endl;
	cout << "creating test..." << endl;
	for (int i=0; i < allocs; ++i) {
		iii a(rand()%allocs, ii(rand()%(max_alloc+1-min_alloc) + min_alloc, i));
		iii b(a.first + rand()%allocs + 1, ii(0, i));
		Q.insert(a);
		Q.insert(b);
	}

	double deltaTime;
    static __int64 gTime,gLastTime;
	__int64 freq;

	cout << "testing...  ";

	for(int x=0; x<X; ++x) {
		tr(Q, it) {
			if(it->second.first) {
				ptr[it->second.second] = ph->alloc(it->second.first);

				if(ptr[it->second.second] == NULL) {
					OK = false;
					goto out2;
				}
				*(char *)(ptr[it->second.second]) = 100;
			} else
				ph->free(ptr[it->second.second]);
		}
	}
out2:
	if(OK) {
		cout << "[OK]" << endl;
    } else {
		cout << "[fail]" << endl;
    	cout << "alloc returned NULL" << endl;
    }
    cout << endl;
}

void test_performance_big(heap876 *ph, int allocs = 10000, int size = 10000) {
	void *ptr;
	bool OK = true;
	cout << "performance big (" << allocs << " allocs of " << size << " bytes): " << endl;

	double deltaTime;
    static __int64 gTime,gLastTime;
	__int64 freq;

	cout << "testing...  ";
	QueryPerformanceCounter((LARGE_INTEGER *)&gLastTime);
	for(int x=0; x<X; ++x) {
		for(int i=0; i<allocs; ++i) {
			ptr = ph->alloc(size);
			if(!ptr) {
				OK = false;
				break;
			}
			ph->free(ptr);
		}
	}

	QueryPerformanceCounter((LARGE_INTEGER *)&gTime);
	QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
    deltaTime = (double)((double)(gTime - gLastTime)/(double)freq);
    if(OK) {
		cout << "[OK]" << endl;
		cout << "TIME = " << deltaTime << endl;
    } else {
		cout << "[fail]" << endl;
    	cout << "alloc returned NULL" << endl;
    }
    cout << endl;
}

void test_poisoning(heap876 *ph) {
	cout << "alloc_debug / poisoning: ";

	int bl_size = 1000;
	int ints = bl_size/sizeof(int);

	int *ptr = (int *)ph->alloc(1000), *ptr2;
	int *end = ptr + ints;
	ph->free(ptr);

	for(int *p = ptr+40; p<end; ++p)
		*p = rand();

	if(ptr2 = (int *)ph->alloc(1000)) {
		if(ptr2 == ptr)
			cout << "failed" << endl;
		else {
			cout << "something strange:" << endl;
			cout << "ptr = alloc(1000)" << endl;
			cout << "free(ptr)" << endl;
			cout << "ptr2 = alloc(1000)" << endl;
			cout << "and after that ptr != ptr2" << endl;
		}
		return;
	} else {
		cout << "[OK]" << endl;
	}
}

void test_badfree(heap876 *ph) {
	cout << "alloc_debug / bad free:  ";

	int *ptr = (int *)ph->alloc(1000);

	ph->free(0);
	ph->free(ptr - 100);
	ph->free(ptr + 1000);
	ph->free(ptr + 3);

	int *ptr2 = (int *)ph->alloc(1000);
	free(ptr);

	ph->free(0);
	ph->free(ptr - 100);
	ph->free(ptr + 1000);
	ph->free(ptr + 3);

	free(ptr2);

	ph->free(0);
	ph->free(ptr - 100);
	ph->free(ptr + 1000);
	ph->free(ptr + 3);

	cout << "[OK]" << endl;
}

void test_nomemory(heap876 *ph) {
	cout << "no memory:  ";

	int *ptr = (int *)ph->alloc(10000);

	int *ptr2 = (int *)ph->alloc(100);
	ph->free(ptr2);

	cout << ( ptr ? "[fail]" : "[OK]" ) << endl;
}

const int t_num = 10;
int main() {
	heap_senya h3(1000000000, heap876::alloc_simple);
	/*test_valid_rand(&h3, 10, 1, 500);
	test_valid_rand(&h3, 100, 1, 100);
	test_valid_rand(&h3, 200, 1, 100);
	test_valid_rand(&h3, 300, 1, 100);*/
	//test_performance_rand(&h3);

	test_performance_rand(&h3, 100000, 1, 100000);

	//test_performance_big(&h3, 100000, 100000000);

	//test_performance_big(&h3, 100, 100000000);

	//heap_senya h0(5000, heap876::alloc_debug);
	//test_nomemory(&h0);
/*
	heap_senya h1(10000, heap876::alloc_debug);
	test_poisoning(&h1);

	heap_senya h2(10000, heap876::alloc_debug);
	test_badfree(&h2);*/

/*
	HANDLE threadlist[t_num];

	for (int i = 0; i < t_num; i++)
	{
		threadlist[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)(test), &h, 0, 0);
	}
	WaitForMultipleObjects(t_num, threadlist, TRUE, INFINITE);
*/
	return 0;
}
